package com.ruoyi.common.utils;

import com.google.gson.annotations.SerializedName;
import com.ruoyi.common.constant.WXConstants;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;

/**
 * 微信JSAPI下单工具类
 * 用于调用微信支付V3接口的合作方JSAPI下单功能，处理请求构建、签名、发送及响应验证
 */
public class WXPartnerJsapiPrepay {
    // 微信支付API基础域名
    private static String HOST = "https://api.mch.weixin.qq.com";
    // 接口请求方法（固定为POST）
    private static String METHOD = "POST";
    // 微信支付V3接口-合作方JSAPI下单接口路径
    private static String PATH = "/v3/pay/partner/transactions/jsapi";

    /**
     * 示例入口方法：演示如何使用当前类发起微信支付JSAPI下单请求
     * 包含客户端初始化、请求参数构建、请求发送及响应处理的完整流程
     */
    public static void main(String[] args) {
        // TODO: 请准备商户开发必要参数，参考：https://pay.weixin.qq.com/doc/v3/partner/4013080340
        // 1. 初始化微信支付客户端
        // 传入商户必要参数：商户号、API证书序列号、私钥路径、微信支付公钥ID及公钥路径
        WXPartnerJsapiPrepay client = new WXPartnerJsapiPrepay(
//                "19xxxxxxxx",                         // 商户号，是由微信支付系统生成并分配给每个商户的唯一标识符，商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/partner/4013080340
//                "1DDE55AD98Exxxxxxxxxx",              // 商户API证书序列号，如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013058924
//                "/resources/to/apiclient_key.pem",    // 商户API证书私钥文件路径，本地文件路径
//                "PUB_KEY_ID_xxxxxxxxxxxxx",           // 微信支付公钥ID，如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013038589
//                "/resources/to/wxp_pub.pem"           // 微信支付公钥文件路径，本地文件路径

                WXConstants.MCHID,                      // 商户号，是由微信支付系统生成并分配给每个商户的唯一标识符，商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/partner/4013080340
                WXConstants.CERTIFICATE_SERIAL_NO,      // 商户API证书序列号，如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013058924
                WXConstants.PRIVATE_KEY_PATH,           // 商户API证书私钥文件路径，本地文件路径
                WXConstants.WECHATPAY_PUBLIC_KEY_ID,    // 微信支付公钥ID，如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013038589
                WXConstants.WECHATPAY_PUBLIC_KEY_PATH   // 微信支付公钥文件路径，本地文件路径
        );

        // 2. 构建JSAPI下单请求对象
        PartnerAPIv3PartnerJsapiPrepayRequest request = new PartnerAPIv3PartnerJsapiPrepayRequest();
        // 2.1 设置基础订单信息（服务商户与子商户参数）
//        request.spAppid = "wx8888888888888888";    // 服务商户应用ID
//        request.spMchid = "1230000109";            // 服务商户号
//        request.subAppid = "wxd678efh567hg6999";   // 子商户应用ID（可选，子商户有独立公众号时填写）
//        request.subMchid = "1900000109";           // 子商户号
        request.spAppid = WXConstants.SP_APPID;     // 服务商户应用ID
        request.spMchid = WXConstants.SP_MCHID;     // 服务商户号
        request.subAppid = WXConstants.SUB_APPID;   // 子商户应用ID（可选，子商户有独立公众号时填写）
        request.subMchid = WXConstants.SUB_MCHID;   // 子商户号

//        request.description = "机器人体验付款";       // 订单描述
        request.description = WXConstants.ORDER_DESCRIPTION;    // 订单描述
//        request.outTradeNo = "1217752501201407033233368018";  // 商户系统内部订单号，需唯一
        request.outTradeNo = WXPayUtility.generateOutTradeNo();
//        request.timeExpire = "2025-12-08T10:34:56+08:00";     // 订单过期时间（ISO 8601格式）
        request.timeExpire = WXPayUtility.generateExpireTime(60 * 60 * 1000);
//        request.attach = "机器人体验付款附加数据";               // 附加数据，回调时会原样返回
        request.attach = WXConstants.ORDER_ATTACH;              // 附加数据，回调时会原样返回

        request.notifyUrl = " https://www.weixin.qq.com/wxpay/pay.php";  // 支付结果通知回调地址
        request.goodsTag = "WXG";                  // 订单优惠标记

        // 2.2 设置结算信息（分账相关）
        request.settleInfo = new PartnerSettleInfo();
        request.settleInfo.profitSharing = true;   // 是否允许分账（true=需要分账）

        // 2.3 设置金额信息
        request.supportFapiao = true;              // 是否支持开票
        request.amount = new CommonAmountInfo();
        request.amount.total = 500L;                         // 订单总金额（单位：分）
//        request.amount.currency = "CNY";                   // 币种（默认CNY人民币）
        request.amount.currency = WXConstants.CURRENCY_CNY;  // 币种（默认CNY人民币）

        // 2.4 设置支付者信息（服务商户与子商户的用户标识）
        request.payer = new PartnerJsapiReqPayerInfo();
//        request.payer.spOpenid = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o";  // 服务商户用户openid
//        request.payer.subOpenid = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"; // 子商户用户openid（子商户有独立公众号时必填）
        request.payer.spOpenid = WXConstants.SP_OPENID;             // 服务商户用户openid
        request.payer.subOpenid = WXConstants.SUB_OPENID;           // 子商户用户openid（子商户有独立公众号时必填）


        // 2.5 设置商品详情
        request.detail = new CouponInfo();
        request.detail.costPrice = 400L;        // 商品成本价格（单位：分）
        request.detail.invoiceId = "微信123";      // 发票ID（可选）
        request.detail.goodsDetail = new ArrayList<>();  // 商品明细列表
        {
            // 添加单个商品明细
            GoodsDetail goodsDetailItem0 = new GoodsDetail();
//            goodsDetailItem0.merchantGoodsId = "1246464644";  // 商户侧商品ID
//            goodsDetailItem0.wechatpayGoodsId = "1001";       // 微信支付商品ID（可选）
            goodsDetailItem0.merchantGoodsId = WXConstants.MERCHANT_GOODS_ID; // 商户侧商品ID
            goodsDetailItem0.wechatpayGoodsId = WXConstants.WECHATPAY_GOODS_ID;// 微信支付商品ID（可选）

            goodsDetailItem0.goodsName = "微信小程序体验5分钟套餐";      // 商品名称
            goodsDetailItem0.quantity = 1L;                   // 商品数量
            goodsDetailItem0.unitPrice = 500L;                // 商品单价（单位：分）
            request.detail.goodsDetail.add(goodsDetailItem0);
        }

        // 2.6 设置场景信息（支付场景相关参数）
        request.sceneInfo = new CommonSceneInfo();

//        request.sceneInfo.payerClientIp = "14.23.150.211";   // 支付者客户端IP
//        request.sceneInfo.deviceId = "013467007045764";      // 设备号（可选）
//        request.sceneInfo.storeInfo = new StoreInfo();       // 门店信息（线下场景必填）
//        request.sceneInfo.storeInfo.id = "1601";             // 门店ID
//        request.sceneInfo.storeInfo.name = "甘肃华悦机器人体验店";    // 门店名称
//        request.sceneInfo.storeInfo.areaCode = "730030";     // 门店地区编码
//        request.sceneInfo.storeInfo.address = "甘肃省兰州市城关区中广商务大厦16层";  // 门店详细地址

        request.sceneInfo.payerClientIp = WXConstants.PAYER_CLIENT_IP;
        request.sceneInfo.deviceId = WXConstants.DEVICE_ID;
        request.sceneInfo.storeInfo = new StoreInfo();                      // 门店信息（线下场景必填）
        request.sceneInfo.storeInfo.id = WXConstants.STORE_ID;              // 门店ID
        request.sceneInfo.storeInfo.name = WXConstants.STORE_NAME;          // 门店名称
        request.sceneInfo.storeInfo.areaCode = WXConstants.STORE_AREA_CODE; // 门店地区编码
        request.sceneInfo.storeInfo.address = WXConstants.STORE_ADDRESS;    // 门店详细地址

        // 3. 执行下单请求并处理结果
        try {
            // 调用run方法发送请求，获取预支付响应（包含prepay_id等核心参数）
            PartnerAPIv3PartnerJsapiPrepayResponse response = client.run(request);
            // 3.1 请求成功：继续业务逻辑（如生成支付参数返回给前端）
            System.out.println("下单成功，预支付ID：" + response.prepayId);
        } catch (WXPayUtility.ApiException e) {
            // 3.2 请求失败：处理API错误（如状态码非2xx，包含错误信息）
            System.err.println("下单失败，错误码：" + e.getErrorCode() + "，错误信息：" + e.getErrorMessage());
            e.printStackTrace();
        }
    }

    /**
     * 执行微信支付JSAPI下单请求
     * 负责构建HTTP请求、添加认证头、发送请求、验证响应签名并解析结果
     *
     * @param request 下单请求参数对象，包含订单详情、金额、支付者等信息
     * @return 微信支付返回的预支付响应对象，包含prepay_id等核心参数
     * @throws WXPayUtility.ApiException 当API返回非2xx状态码时抛出（包含状态码、响应体和头信息）
     * @throws UncheckedIOException      当HTTP请求发送失败时抛出（如网络异常）
     */
    public PartnerAPIv3PartnerJsapiPrepayResponse run(PartnerAPIv3PartnerJsapiPrepayRequest request) {
        String uri = PATH;
        String reqBody = WXPayUtility.toJson(request);

        Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
        reqBuilder.addHeader("Accept", "application/json");
        reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody));
        reqBuilder.addHeader("Content-Type", "application/json");
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
        reqBuilder.method(METHOD, requestBody);
        Request httpRequest = reqBuilder.build();

        // 发送HTTP请求
        OkHttpClient client = new OkHttpClient.Builder().build();
        try (Response httpResponse = client.newCall(httpRequest).execute()) {
            String respBody = WXPayUtility.extractBody(httpResponse);
            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
                // 2XX 成功，验证应答签名
                WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey, httpResponse.headers(), respBody);

                // 从HTTP应答报文构建返回数据
                return WXPayUtility.fromJson(respBody, PartnerAPIv3PartnerJsapiPrepayResponse.class);
            } else {
                throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
            }
        } catch (IOException e) {
            throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
        }
    }

    // 商户号（由微信支付分配的唯一标识符）
    private final String mchid;
    // 商户API证书序列号（用于标识商户证书）
    private final String certificateSerialNo;
    // 商户API私钥（用于请求签名生成）
    private final PrivateKey privateKey;
    // 微信支付公钥ID（用于验证响应签名）
    private final String wechatPayPublicKeyId;
    // 微信支付公钥（用于验证响应签名）
    private final PublicKey wechatPayPublicKey;

    /**
     * 构造微信支付JSAPI下单客户端
     *
     * @param mchid                      商户号（微信支付分配）
     * @param certificateSerialNo        商户API证书序列号
     * @param privateKeyFilePath         商户API私钥文件路径（本地PKCS#8格式文件）
     * @param wechatPayPublicKeyId       微信支付公钥ID
     * @param wechatPayPublicKeyFilePath 微信支付公钥文件路径（本地PKCS#8格式文件）
     */
    public WXPartnerJsapiPrepay(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
        this.mchid = mchid;
        this.certificateSerialNo = certificateSerialNo;
        this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
        this.wechatPayPublicKeyId = wechatPayPublicKeyId;
        this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
    }

    /**
     * 微信支付V3接口-合作方JSAPI下单请求参数封装
     * 对应API文档中的请求体结构：https://pay.weixin.qq.com/doc/v3/apis/chapter4_1_1.shtml
     */
    public static class PartnerAPIv3PartnerJsapiPrepayRequest {
        /**
         * 服务商户应用ID
         * 由微信开放平台分配的唯一标识符（wx开头），服务商户在微信开放平台注册的APPID
         */
        @SerializedName("sp_appid")
        public String spAppid;

        /**
         * 服务商户号
         * 微信支付分配给服务商户的唯一标识符，以19位纯数字组成
         */
        @SerializedName("sp_mchid")
        public String spMchid;

        /**
         * 子商户应用ID
         * 子商户有独立公众号时填写（wx开头），子商户无独立公众号可不填
         */
        @SerializedName("sub_appid")
        public String subAppid;

        /**
         * 子商户号
         * 微信支付分配给子商户的唯一标识符，以19位纯数字组成，必填
         */
        @SerializedName("sub_mchid")
        public String subMchid;

        /**
         * 订单描述
         * 展示在支付凭证上的简短描述（如"腾讯视频会员充值"），必填
         */
        @SerializedName("description")
        public String description;

        /**
         * 商户订单号
         * 商户系统内部唯一订单号，32个字符内，只能包含字母、数字、下划线，必填
         */
        @SerializedName("out_trade_no")
        public String outTradeNo;

        /**
         * 订单过期时间
         * 格式为ISO 8601标准（yyyy-MM-ddTHH:mm:ss+08:00），如"2024-01-01T12:00:00+08:00"，非必填
         */
        @SerializedName("time_expire")
        public String timeExpire;

        /**
         * 附加数据
         * 商户自定义附加信息，回调通知时会原样返回，可用于传递订单额外参数，非必填
         */
        @SerializedName("attach")
        public String attach;

        /**
         * 通知回调地址
         * 支付结果异步通知接收地址，需为HTTPS协议，微信支付会向该地址推送支付结果通知，必填
         */
        @SerializedName("notify_url")
        public String notifyUrl;

        /**
         * 订单优惠标记
         * 用于指定当前订单参与的优惠活动（如"WXG"表示微信支付优惠），非必填
         */
        @SerializedName("goods_tag")
        public String goodsTag;

        /**
         * 结算信息对象
         * 包含分账相关配置（如是否允许分账），需分账时必填
         */
        @SerializedName("settle_info")
        public PartnerSettleInfo settleInfo;

        /**
         * 是否支持开票
         * true表示支持开票，false或不填表示不支持，非必填
         */
        @SerializedName("support_fapiao")
        public Boolean supportFapiao;

        /**
         * 金额信息对象
         * 包含订单总金额及币种，必填
         */
        @SerializedName("amount")
        public CommonAmountInfo amount;

        /**
         * 支付者信息对象
         * 包含服务商户和子商户的用户标识（openid），必填
         */
        @SerializedName("payer")
        public PartnerJsapiReqPayerInfo payer;

        /**
         * 订单详情对象
         * 包含商品成本、发票信息及商品明细列表，非必填
         */
        @SerializedName("detail")
        public CouponInfo detail;

        /**
         * 场景信息对象
         * 包含支付场景相关参数（如客户端IP、设备号、门店信息等），线下场景建议填写
         */
        @SerializedName("scene_info")
        public CommonSceneInfo sceneInfo;
    }

    /**
     * 微信支付V3接口-合作方JSAPI下单响应结果封装
     */
    public static class PartnerAPIv3PartnerJsapiPrepayResponse {
        @SerializedName("prepay_id")
        public String prepayId;
    }

    /**
     * 结算信息类
     * 用于配置订单分账相关参数
     */
    public static class PartnerSettleInfo {
        @SerializedName("profit_sharing")
        public Boolean profitSharing;
    }

    /**
     * 金额信息封装类
     * 包含订单总金额及币种信息
     */
    public static class CommonAmountInfo {
        /**
         * 订单总金额
         * 单位为分（如100表示1元），整数类型，必填
         */
        @SerializedName("total")
        public Long total;

        /**
         * 币种
         * 遵循ISO 4217标准，默认值为"CNY"（人民币），非必填
         */
        @SerializedName("currency")
        public String currency;
    }

    /**
     * 支付者信息类
     * 包含服务商户和子商户的用户标识
     */
    public static class PartnerJsapiReqPayerInfo {
        /**
         * 服务商户用户OpenID
         * 服务商户公众号下的用户唯一标识，通过微信授权获取（如微信网页授权接口）
         */
        @SerializedName("sp_openid")
        public String spOpenid;

        /**
         * 子商户用户OpenID
         * 子商户公众号下的用户唯一标识，子商户有独立公众号时必填，通过子商户公众号授权获取
         */
        @SerializedName("sub_openid")
        public String subOpenid;
    }

    /**
     * 订单详情信息类
     * 包含商品成本、发票信息及商品明细列表
     */
    public static class CouponInfo {
        /**
         * 商品成本价格
         * 单位为分（如608800表示6088元），用于商户内部成本核算，非必填
         */
        @SerializedName("cost_price")
        public Long costPrice;

        /**
         * 发票ID
         * 关联订单对应的发票标识，用于后续开票流程，非必填
         */
        @SerializedName("invoice_id")
        public String invoiceId;

        /**
         * 商品明细列表
         * 包含订单中每个商品的详细信息（如商户商品ID、名称、单价、数量等），非必填但建议提供
         */
        @SerializedName("goods_detail")
        public List<GoodsDetail> goodsDetail;
    }

    /**
     * 场景信息类
     * 包含支付场景相关参数（客户端IP、设备号、门店信息等）
     */
    public static class CommonSceneInfo {
        /**
         * 支付者客户端IP地址
         * 格式为IPv4或IPv6，用于定位支付请求来源，建议填写真实用户IP
         */
        @SerializedName("payer_client_ip")
        public String payerClientIp;

        /**
         * 支付设备号
         * 如门店POS机编号、自助终端设备ID等，线下场景可填写，非必填
         */
        @SerializedName("device_id")
        public String deviceId;

        /**
         * 门店信息对象
         * 线下实体门店场景必填，包含门店唯一标识、名称、地区编码及详细地址
         */
        @SerializedName("store_info")
        public StoreInfo storeInfo;
    }

    /**
     * 商品明细信息类
     * 描述单个商品的详细信息（名称、单价、数量等）
     */
    public static class GoodsDetail {
        /**
         * 商户侧商品ID
         * 商户系统内部商品唯一标识，必填
         */
        @SerializedName("merchant_goods_id")
        public String merchantGoodsId;

        /**
         * 微信支付商品ID
         * 微信支付平台备案的商品ID，可选
         */
        @SerializedName("wechatpay_goods_id")
        public String wechatpayGoodsId;

        /**
         * 商品名称
         * 商品的展示名称，会显示在支付凭证等场景，必填
         */
        @SerializedName("goods_name")
        public String goodsName;

        /**
         * 商品数量
         * 购买的商品数量，整数类型，必填
         */
        @SerializedName("quantity")
        public Long quantity;

        /**
         * 商品单价
         * 单位为分（如528800表示5288元），整数类型，必填
         */
        @SerializedName("unit_price")
        public Long unitPrice;
    }

    /**
     * 门店信息类
     * 描述订单关联的物理门店信息（用于线下场景对账和统计）
     */
    public static class StoreInfo {
        /**
         * 门店唯一标识ID
         * 商户系统内部门店的唯一编码，线下场景必填，用于区分不同门店
         */
        @SerializedName("id")
        public String id;

        /**
         * 门店名称
         * 门店的对外展示名称，需与实际门店招牌一致，会显示在支付凭证等场景
         */
        @SerializedName("name")
        public String name;

        /**
         * 门店地区编码
         * 遵循国家行政区划编码标准（如440305表示广东省深圳市南山区），用于定位门店所属区域
         */
        @SerializedName("area_code")
        public String areaCode;

        /**
         * 门店详细地址
         * 包含省、市、区、街道、门牌号的完整物理地址，支持精确到楼层或店铺编号
         */
        @SerializedName("address")
        public String address;
    }
}